/**
 * 许可证模型
 *
 * Created by snowingsea on 2020/02/24.
 */
const moment = require('moment');
const License = require('./mongo/license');
const hydra = require('./hydra');
const publisher = require('./redis/publisher');
const utility = require('./utility');
const auth = require('./auth');
const rules = require('./rules');
const logger = require('./logger');
const template = require('./analyze-templates');

/**
 * 许可证状态类型
 * @type {Object}
 */
const licenseStatusKind = {
  normal: 0,
  illegal: 1,
  notMatch: 2,
  exceedTime: 4,
  exceedTester: 8,
  systemTimeTampering: 16,
};

const licenseModuleStatusKind = {
  normal: 0,
  exceedTime: 1,
  beforeUsableTime: 2,
  systemTimeTampering: 3,
  submoduleError: 4,
  toBeExpire: 5,
};

const toBeExpireDays = 3;

/**
 * 许可证错误码
 * @type {Object}
 */
const licenseErrorCode = {
  notMatch: 13,
};

const moduleType = {
  'unit-test': 'unitTest',
  'static-check': 'staticAnalyze',
  'integration-test': 'integrationTest',
};

function isEducationVersion(license) {
  return license.version && (license.version === 'basic' || license.version === 'full');
}
function buildLicense(licenseInstance, originalData) {
  licenseInstance.companyName = originalData.company;
  licenseInstance.huaweiLicenseCode = originalData.huaweiLicenseCode;
  licenseInstance.cpuLimit = originalData.cpu;
  licenseInstance.userLimit = originalData.user;
  licenseInstance.language = originalData.language || [];
  licenseInstance.licenseSCD = originalData.licenseSCD || 0;
  licenseInstance.modules = {};
  const combineModuleLanguage = originalData.language === undefined;
  if (originalData.version) {
    licenseInstance.version = originalData.version;
  }
  if (originalData.testReport) {
    licenseInstance.testReport = originalData.testReport;
  }
  if (!originalData.modules) {
    return;
  }
  Object.keys(originalData.modules).forEach((module) => {
    if (!moduleType[module]) {
      return;
    }
    const key = moduleType[module];
    licenseInstance.modules[key] = {
      expiresDate: Number(originalData.modules[module].expired),
      startDate: Number(originalData.modules[module].startTime),
      status: licenseModuleStatusKind.normal,
    };
    // 静态分析模块数据处理
    if (originalData.modules[module].modules) {
      licenseInstance.modules[key].modules = originalData.modules[module].modules;
    }
    // 导出测试用例格式
    if (originalData.modules[module]['case-format']) {
      licenseInstance.modules[key].allowedExportFormat = originalData.modules[module]['case-format'];
    }
    // 处理导入测试用例格式
    if (originalData.modules[module]['case-import-format']) {
      licenseInstance.modules[key].allowedImportFormat = originalData.modules[module]['case-import-format'];
    }
    if (originalData.modules[module].modules) {
      licenseInstance.modules[key].status = licenseModuleStatusKind.submoduleError;
      Object.values(originalData.modules[module].modules).forEach(({ status }) => {
        if (status === 0) {
          licenseInstance.modules[key].status = licenseModuleStatusKind.normal;
        }
      });
    }
    if (combineModuleLanguage && originalData.modules[module].language) {
      licenseInstance.language = utility.arrayDeduplicate(
        licenseInstance.language.concat(originalData.modules[module].language)
      );
    }
    if (originalData.modules[module].advanced) {
      licenseInstance.modules[key].advanced = originalData.modules[module].advanced;
    }
  });
  utility.updateUserDefined(originalData);
}

/**
 * 读取许可证信息
 * @param {String} strLicense 许可证字符串
 * @return {Promise}
 */
function readLicense(strLicense) {
  return new Promise((resolve, reject) => {
    const license = new License({ status: licenseStatusKind.normal });
    hydra.registerLicense(strLicense)
      .then((res) => {
        license.licenseHash = utility.md5(res);
        res = JSON.parse(res);
        buildLicense(license, res);
        return Promise.resolve();
      }, (err) => {
        if (err instanceof hydra.types.InvalidLicense || err instanceof hydra.types.SystemFault) {
          switch (err.error_code) {
            case licenseErrorCode.notMatch:
              license.status = licenseStatusKind.notMatch;
              break;
            default:
              license.status = licenseStatusKind.illegal;
              break;
          }
          return Promise.resolve();
        }
        return Promise.reject(err);
      })
      .then(() => {
        resolve(license);
      })
      .catch(reject);
  });
}

/**
 * 获取json信息
 * @param {License} license 许可证
 * @returns {Object}
 */
function getJson(license) {
  const json = {
    licenseStatus: license.status,
    licenseSCD: license.licenseSCD || 0,
    license: {},
  };
  switch (license.status) {
    case licenseStatusKind.illegal:
    case licenseStatusKind.notMatch:
      json.license.modules = {};
      break;
    default:
      license.modules = license.modules || {};
      Object.keys(license.modules).forEach((module) => {
        license.modules[module].expiresDate = moment(license.modules[module].expiresDate).format();
        license.modules[module].startDate = moment(license.modules[module].startDate).format();
      });
      json.license = {
        companyName: license.companyName,
        huaweiLicenseCode: license.huaweiLicenseCode,
        cpuLimit: license.cpuLimit,
        userLimit: license.userLimit,
        modules: {},
      };
      if (license.modules.staticAnalyze) {
        json.license.modules.staticAnalyze = license.modules.staticAnalyze;
      }
      if (license.modules.unitTest) {
        json.license.modules.unitTest = license.modules.unitTest;
      }
      if (license.modules.integrationTest) {
        json.license.modules.integrationTest = license.modules.integrationTest;
      }
      if (license.language) {
        json.license.language = license.language;
      }
      if (isEducationVersion(license)) {
        // 教育版
        json.license.version = license.version;
      }
      if (license.testReport) {
        json.license.testReport = license.testReport;
      }
      break;
  }
  return json;
}

/**
 * 更新许可证状态
 * @param {License} license 许可证信息
 * @return {Promise}
 */
function updateLicenseStatus(license) {
  let expired = true;
  license.modules = license.modules || {};
  Object.keys(license.modules).forEach((module) => {
    let tmpDate = new Date(license.modules[module].expiresDate + 86400000);
    const expriesTime = new Date(`${tmpDate.getFullYear()}-${tmpDate.getMonth() + 1}-${tmpDate.getDate()}`).getTime();
    tmpDate = new Date(license.modules[module].startDate);
    const startTime = new Date(`${tmpDate.getFullYear()}-${tmpDate.getMonth() + 1}-${tmpDate.getDate()}`).getTime();
    if (license.modules[module] === licenseModuleStatusKind.submoduleError) {
      // 模块的子模块（第三方）有问题，此类问题优先级最高
    } else if (expriesTime <= Date.now()) {
      license.modules[module].status = licenseModuleStatusKind.exceedTime;
    } else if (startTime > Date.now()) {
      license.modules[module].status = licenseModuleStatusKind.beforeUsableTime;
    } else {
      // 只要有一个module没过期，就视作license没过期
      expired = false;
      if (expriesTime - toBeExpireDays * 86400 * 1000 <= Date.now()) {
        license.modules[module].status = licenseModuleStatusKind.toBeExpire;
      }
    }
  });
  switch (license.status) {
    case licenseStatusKind.illegal:
    case licenseStatusKind.notMatch:
      break;
    default:
      license.status = licenseStatusKind.normal;
      if (expired) {
        license.status |= licenseStatusKind.exceedTime; // eslint-disable-line no-bitwise
      }
      break;
  }
  return license;
}
/**
 * 处理许可证对测试用例导出格式，兼容老的许可证
 */
async function handleAllowedImportFormat() {
  const license = await License.findOne();
  if (!license) {
    return;
  }
  license.modules = license.modules || {};
  let modify = false;
  Object.keys(license.modules).forEach((key) => {
    if (['integrationTest', 'unitTest'].includes(key)
    && !license.modules[key].allowedImportFormat
    && license.modules[key].allowedExportFormat) {
      license.modules[key].allowedImportFormat = [];
      license.modules[key].allowedExportFormat.forEach((format) => {
        if (format === 'srt') {
          modify = true;
          license.modules[key].allowedImportFormat.push(format);
        }
        if (format === 'xlsx') {
          modify = true;
          license.modules[key].allowedImportFormat.push('xlsx-huicheng');
        }
      });
    }
  });
  if (modify) {
    await License.updateOne({ _id: license._id }, { $set: { modules: license.modules } });
  }
}
/**
 * 获取许可证
 * @return {Promise<License>}
 */
function getLicense() {
  return new Promise((resolve, reject) => {
    handleAllowedImportFormat()
      .then(() => License.findOne().lean())
      .then(async (license) => {
        if (!license) {
          // 兼用免费版本自动查询许可证
          /* eslint no-use-before-define: ["error", { "functions": false }] */
          await checkLicenseStatus();
          license = await License.findOne().lean();
        }
        if (!license) {
          return Promise.resolve();
        }
        // 关闭旧版许可证静态分析不支持多规则集的静态分析功能模块
        if (license.modules && license.modules.staticAnalyze
        && !license.modules.staticAnalyze.modules) {
          delete license.modules.staticAnalyze;
        }
        // 兼容旧许可证
        return updateLicenseStatus(license);
      })
      .then(resolve)
      .catch(reject);
  });
}

/**
 * 删除许可证
 * @return {Promise}
 */
async function removeLicense() {
  await License.deleteMany();
  publisher.publishLicenseMessage('remove');
  await rules.initRulesByFile('TJ_DW230-2020-C.csv');
  await auth.removeExceededTesterToken(0);
}

async function operationAfterUpdateLicense() {
  return template.updatePreferenceTemplate();
}

/**
 * 通过文件更新许可证
 * @param {String} filePath 文件路径
 * @return {Promise}
 */
function replaceLicenseByFile(filePath) {
  return new Promise((resolve, reject) => {
    License.deleteMany()
      .then(() => utility.readFile(filePath))
      .then((content) => readLicense(content))
      .then((license) => license.save())
      .then((license) => updateLicenseStatus(license))
      .then(async (license) => {
        await rules.initRulesByFile('TJ_DW230-2020-C.csv');
        await operationAfterUpdateLicense();
        return license;
      })
      .then((license) => {
        publisher.publishLicenseMessage('update', getJson(license));
        resolve(license);
        auth.removeExceededTesterToken(license.userLimit);
      })
      .catch(reject);
  });
}

/**
 * 通过许可证内容更新许可证
 * @param {String} strLicense 许可证字符串
 * @return {Promise}
 */
function replaceLicenseByString(strLicense) {
  return new Promise((resolve, reject) => {
    License.deleteMany()
      .then(() => readLicense(strLicense))
      .then((license) => license.save())
      .then((license) => updateLicenseStatus(license))
      .then(async (license) => {
        await rules.initRulesByFile('TJ_DW230-2020-C.csv');
        await operationAfterUpdateLicense();
        return license;
      })
      .then((license) => {
        publisher.publishLicenseMessage('update', getJson(license));
        resolve(license);
        auth.removeExceededTesterToken(license.userLimit);
      })
      .catch(reject);
  });
}

function updateLicenseByLicenseQuery(licenseData) {
  const licenseInstance = new License({
    status: licenseStatusKind.normal,
    licenseHash: licenseData.licenseHash,
  });
  return new Promise((resolve, reject) => {
    License.deleteMany()
      .then(() => buildLicense(licenseInstance, licenseData))
      .then(() => licenseInstance.save())
      .then((license) => updateLicenseStatus(license))
      .then(async (license) => {
        await rules.initRulesByFile('TJ_DW230-2020-C.csv');
        await operationAfterUpdateLicense();
        return license;
      })
      .then((license) => {
        publisher.publishLicenseMessage('update', getJson(license));
        resolve(license);
        auth.removeExceededTesterToken(license.userLimit);
      })
      .catch(reject);
  });
}

function getLicenseStatusJson(license) {
  const json = {
    licenseStatus: license.status,
    licenseSCD: license.licenseSCD || 0,
    license: {},
  };
  switch (license.status) {
    case licenseStatusKind.illegal:
    case licenseStatusKind.notMatch:
      json.license.modules = {};
      break;
    default:
      license.modules = license.modules || {};
      Object.keys(license.modules).forEach((module) => {
        delete license.modules[module].expiresDate;
        delete license.modules[module].startDate;
      });
      json.license.modules = license.modules;
      if (license.language) {
        json.license.language = license.language;
      }
      if (isEducationVersion(license)) {
        json.license.version = license.version;
      }
      if (license.testReport) {
        json.license.testReport = license.testReport;
      }
      break;
  }
  return json;
}
/**
 * 获取许可证可用的静态分析规则集
 * @returns 返回可用的静态分析规则集
 */
async function getStaticAnalyzeRuleSetByLicense() {
  const license = await License.findOne().lean();
  const ruleSets = [];
  if (license.modules
    && license.modules.staticAnalyze
    && license.modules.staticAnalyze.status === licenseModuleStatusKind.normal
    && license.modules.staticAnalyze.modules) {
    Object.keys(license.modules.staticAnalyze.modules).forEach((key) => {
      const item = license.modules.staticAnalyze.modules[key];
      if (item.status === licenseModuleStatusKind.normal) {
        ruleSets.push(...item.specs);
      }
    });
  }
  return ruleSets;
}
async function getAllowedImportFormatByLicense(versionType) {
  const license = await License.findOne().lean();
  const versionConfig = license.modules[versionType];
  if (!versionConfig) {
    return {};
  }
  return versionConfig.allowedImportFormat;
}
async function checkLicenseStatus() {
  const originLicense = await License.findOne({}, { _id: 0 }).lean();
  const hydraLicense = {};
  try {
    hydraLicense.license = await hydra.getLicenseStatus();
  } catch (err) {
    // no license or interface error
    if (err instanceof hydra.types.InvalidLicense) {
      if (originLicense && originLicense.status !== licenseStatusKind.notMatch && originLicense.status !== licenseStatusKind.illegal) {
        await removeLicense();
      }
    } else {
      logger.error('license query hydra error:', err);
    }
    return Promise.resolve();
  }
  hydraLicense.licenseHash = utility.md5(hydraLicense.license);
  if (originLicense && originLicense.licenseHash === hydraLicense.licenseHash) {
    return Promise.resolve();
  }
  await updateLicenseByLicenseQuery({ ...JSON.parse(hydraLicense.license), licenseHash: hydraLicense.licenseHash });
  return Promise.resolve();
}
/**
 * Export model definition object.
 */
module.exports = {
  statusKind: licenseStatusKind,
  moduleStatusKind: licenseModuleStatusKind,
  getStaticAnalyzeRuleSetByLicense,
  getJson,
  getLicense,
  getLicenseStatusJson,
  getAllowedImportFormatByLicense,
  replaceLicenseByFile,
  replaceLicenseByString,
  removeLicense,
  isEducationVersion,
  checkLicenseStatus,
};

exports = Object.assign(exports, module.exports);
